﻿#region License
// Copyright (c) 2009 Shengmin Zhang
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Web;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Web.Caching;
using System.IO;
using System.Text;


//using Apprender.Web.Configuration;
using Apprender.Compression;
//using Apprender.Utilities;

namespace Apprender.Web.Caching
{
    public class CacheProvider : ICacheProvider
    {
        private const string CACHE_EXT = ".cache";
        //private const string GZIP_EXT = ".gz";

        public CacheProvider() { }


        //private string _defaultCacheDirectory = "~/__apprenderwebcache";
        public virtual string DefaultCacheDirectory
        {
            get { return "~/__apprenderwebcache"; }
        }

        #region ICacheProvider Members

        public virtual byte[] GetCache(string cacheKey, HttpContext context, ICacheSetting setting)
        {
            var mode = setting.CacheMode;
            byte[] result = null;
            if ((mode & CacheMode.Memory) == CacheMode.Memory)
            {
                result = GetCacheFromMemory(cacheKey, context.Cache);
            }
            if (result == null && (mode & CacheMode.Disk) == CacheMode.Disk)
            {
                result = GetCacheFromDisk(cacheKey, context, setting);
            }

            return result;
        }

        public virtual void RemoveCache(string cacheKey, HttpContext context, string cacheDirectory)
        {
            context.Cache.Remove(cacheKey);
           
            string path = GetFilePath(cacheKey, context.Server.MapPath(cacheDirectory));

            if (File.Exists(path))
            {
                File.Delete(path);
            }
            
        }

        public virtual void InsertCache(string cacheKey, byte[] item, HttpContext context, ICacheSetting setting)
        {
            var mode = setting.CacheMode;

            if ((mode & CacheMode.Memory) == CacheMode.Memory)
            {
                InsertCacheToMemory(cacheKey, item, context, setting);
            }
            if ((mode & CacheMode.Disk) == CacheMode.Disk)
            {
                InsertCacheToDisk(cacheKey, item, context, setting);
            }


        }

        public virtual string GetCacheKey(Dictionary<string, string> paramList, string fullTypeName, string methodName, CompressionEncoding encoding, string[] keys)
        {
            if (keys != null)
            {
                var length = keys.Length;
                var sb = new StringBuilder(length * 20);
                sb.Append(fullTypeName);
                sb.Append('.');
                sb.Append(methodName);
                

                for (int i = 0; i < length; i++)
                {
                    var key = keys[i];
                    sb.Append('&');
                    sb.Append(key);
                    sb.Append('=');
                    sb.Append(HttpUtility.UrlEncode(paramList[key]));
                }

                sb.Append("&ce=");
                sb.Append(encoding);
                return sb.ToString();
            }
            else
            {
                var length = paramList.Count;
                StringBuilder sb = new StringBuilder(length * 20);
                sb.Append(fullTypeName);
                sb.Append('.');
                sb.Append(methodName);
                
                foreach (var p in paramList)
                {
                    sb.Append('&');
                    sb.Append(p.Key);
                    sb.Append('=');
                    sb.Append(HttpUtility.UrlEncode(p.Value));
                }
                sb.Append("&ce=");
                sb.Append(encoding);
                return sb.ToString();
            }
            
        }

        public virtual string GetCacheKey(NameValueCollection paramList, string fullTypeName, string methodName,CompressionEncoding encoding, string[] keys)
        {
            if (keys != null)
            {
                var length = keys.Length;
                var sb = new StringBuilder(length * 20);
                sb.Append(fullTypeName);
                sb.Append('.');
                sb.Append(methodName);
                
                for (int i = 0; i < length; i++)
                {
                    var key = keys[i];
                    sb.Append('&');
                    sb.Append(key);
                    sb.Append('=');
                    sb.Append(paramList[key]);
                }
                sb.Append("&ce=");
                sb.Append(encoding);
                return sb.ToString();
            }
            else
            {
                var length = paramList.Count;
                StringBuilder sb = new StringBuilder(length * 20);
                sb.Append(fullTypeName);
                sb.Append('.');
                sb.Append(methodName);
                
                for (int i = 0; i < length; i++)
                {
                    var key = paramList.GetKey(i);
                    sb.Append('&');
                    sb.Append(key);
                    sb.Append('=');
                    sb.Append(HttpUtility.UrlEncode(paramList.Get(i)));
                }
                sb.Append("&ce=");
                sb.Append(encoding);
                return sb.ToString();
            }
        }



        

        #endregion

        private byte[] GetCacheFromMemory(string cacheKey, Cache cache)
        {
            return cache[cacheKey] as byte[];

        }

        private byte[] GetCacheFromDisk(string cacheKey, HttpContext context, ICacheSetting setting)
        {
            string dir = context.Server.MapPath(setting.CacheDirectory);
            if (Directory.Exists(dir))
            {
                FileInfo file = new FileInfo(GetFilePath(cacheKey,dir));

                if (file.Exists)
                {
                    var stream = file.OpenRead();
                    var length = (int)stream.Length;
                    var result = new byte[length];
                    try
                    {
                        stream.Read(result, 0, length);
                    }
                    finally
                    {
                        stream.Close();
                    }

                    return result;
                }
            }

            return null;



        }

        private void InsertCacheToDisk(string cacheKey, byte[] item, HttpContext context, ICacheSetting setting)
        {
            string cacheDirectory = setting.CacheDirectory;
            var dir = context.Server.MapPath(cacheDirectory);
            var path = GetFilePath(cacheKey, dir);
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);

            FileInfo file = new FileInfo(path);

            if (!file.Exists)
            {
                FileStream stream = file.Create();

                try
                {
                    stream.Write(item, 0, item.Length);
                }
                finally
                {
                    stream.Close();
                }
            }
        }

        private void InsertCacheToMemory(string cacheKey, byte[] item, HttpContext context, ICacheSetting setting)
        {
            if (setting.CacheDuration != 0)
            {
                if (setting.UseSlidingCache)
                    context.Cache.Insert(cacheKey, item, null, Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(setting.CacheDuration), setting.CacheItemPriority, null);
                else
                    context.Cache.Insert(cacheKey, item, null, DateTime.UtcNow.AddSeconds(setting.CacheDuration), Cache.NoSlidingExpiration, setting.CacheItemPriority, null);
            }
        }

        private string GetFilePath(string cacheKey, string dir)
        {
            
                return dir + @"\" + cacheKey + CACHE_EXT;
        }


        #region ICacheProvider Members




        #endregion
    }
}
